import type { Request, Response, NextFunction } from "express";
import { authVerification } from "./auth";
import assert from "assert";
import { commLog } from "../../utils/log";
import z from "zod";
import MessageCode from "../../validators/core/MessageCode";

/** 导入控制器注册模块 */
import("./register");

/** RPC 方法 */
export type TRpcMethod = (
  req: Request,
  res: Response,
  next: NextFunction
) => Promise<void>;

/** 控制器注册参数 */
export interface IRpcRegisterParams {
  /** 控制器名称 */
  name: string;
  /** 控制方法 */
  method: TRpcMethod;
  /** 是否需要签权验证 */
  auth: boolean;
  /** 控制器描述 */
  desc: string;
}

/** 控制器方法集合 */
const rpcMethods = new Map<string, IRpcRegisterParams>();

/** 注册控制器方法 */
export const registerRpcMethod = (params: IRpcRegisterParams[]) => {
  params.forEach((item) => rpcMethods.set(item.name, item));

  // 输出接口占用内存
  // const blob = new Blob([buildServerAPICode()], { type: 'text/plain' })
};

const JsonRPCValid = z.object({
  method: z.string({ required_error: MessageCode.Public.method.required }),
});

/** 根据传入的请求对象，调用对应的控制器方法 */
export const dispatchRpc = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // 校验请求参数是否正确
  JsonRPCValid.passthrough().parse(req.body);

  const Rpc = req.body as IReqJsonRpc;

  // 根据请求数据中的 method 获取控制器方法
  const { method, auth } = rpcMethods.get(Rpc.method) || {};

  // 检擦函数方法是否注册
  assert(method, MessageCode.Public.method.notFound);

  // 检查是否授权
  if (auth) assert(await authVerification(req), MessageCode.Public.method.auth);

  // 执行控制器方法
  if (method) {
    commLog.trace(`调用方法：${method.name}`);
    await method(req, res, next);
  }

  next();
};

/** 生成单行控制器方法注释 */
export const createMathodDescOneLineDesc = (desc: string = ""): string => {
  return desc === ""
    ? "\n"
    : `
/** ${desc} */`;
};

/** 生成服务端API列表代码 */
export const buildServerAPICode = () => {
  const items = Array.from(rpcMethods.values());

  const moban = `
/**
* 服务端API
* 
* 调用者统一使用 POST 向 http://localhost:${process.env.PORT}/${
    process.env.API_URL
  } 发送如下 JSON 格式
* 
* \`\`\`JSON
* {
*   method: string, // RPC方法名称，也是API名称
*   params: Object, // 方法参数
* }
* \`\`\`
* 
* API中传入的参数均为该 JSON 格式的 params 值
* 
*/
export const SERVER_API = {
  ${items
    .map((item) => `${item.desc}\n${item.name}: '${item.name}',`)
    .join("\n")}
}`;
  return moban;
};
